package com.isp.org.server.application.facade;

import cn.hutool.core.util.ObjectUtil;
import com.isp.common.model.constant.CommonConstant;
import com.isp.common.model.exception.APIRuntimeException;
import com.isp.common.model.vo.Result;
import com.isp.org.api.dto.Resource;
import com.isp.org.api.dto.menu.SysMenu;
import com.isp.org.api.dto.role.SysRole;
import com.isp.org.api.dto.user.SysUserId;
import com.isp.org.api.enums.MenuTypeEnums;
import com.isp.org.server.domain.entity.MenuEntity;
import com.isp.org.server.domain.entity.RoleMenuEntity;
import com.isp.org.server.domain.service.MenuService;
import com.isp.org.server.domain.service.RoleMenuService;
import com.isp.org.server.domain.service.RoleUserService;
import com.isp.org.server.interfaces.model.ResultCodeEnum;
import com.isp.org.server.interfaces.model.menu.mapper.MenuMapStruct;
import com.isp.org.server.interfaces.model.menu.vo.BaseMenuVO;
import com.isp.org.server.interfaces.model.menu.vo.request.MenuCreateRequest;
import com.isp.org.server.interfaces.model.menu.vo.request.MenuUpdateRequest;
import com.isp.org.server.interfaces.model.menu.vo.response.MenuInfoResponse;
import com.isp.org.server.interfaces.model.menu.vo.response.MenuTreeResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * @author linsy
 * @version 1.0
 * @created 2024/1/23 10:20
 */
@Service
@RequiredArgsConstructor
public class MenuFacade {

    private final MenuService menuService;
    private final RoleMenuService roleMenuService;
    private final RoleUserService roleUserService;

    /**
     * 构建菜单树
     *
     * @param sysMenus 菜单
     * @return 菜单树
     */
    public static List<MenuTreeResponse> treeBuilder(List<MenuTreeResponse> sysMenus) {
        List<MenuTreeResponse> menus = new ArrayList<>();
        for (MenuTreeResponse sysMenu : sysMenus) {
            if (ObjectUtil.equal(-1L, sysMenu.getParentId())) {
                menus.add(sysMenu);
            }
            for (MenuTreeResponse menu : sysMenus) {
                if (menu.getParentId().equals(sysMenu.getId())) {
                    if (sysMenu.getSubMenus() == null) {
                        sysMenu.setSubMenus(new ArrayList<>());
                    }
                    sysMenu.getSubMenus().add(menu);
                }
            }
        }
        return menus;
    }

    /**
     * 根据角色code查询
     *
     * @param app       app
     * @param roleCodes 角色code
     * @param type      类型
     * @return 菜单
     */
    public List<SysMenu> findByRoleCodes(String app, Set<String> roleCodes, Integer type) {
        // 当前角色是超级管理员，拥有所有权限
        if (roleCodes.contains(CommonConstant.ADMIN_ROLE_NAME)) {
            return menuService.findMenusByType(app, type);
        }
        return roleMenuService.findMenusByRoleCodes(app, roleCodes, type);
    }

    /**
     * 根据id删除
     *
     * @param id 菜单id
     */
    public void removeById(String id) {
        menuService.deleteById(id);
    }

    /***
     * 根据角色id集合查询菜单
     *
     * @param roleIds 角色id集合
     * @return 菜单
     */
    public List<SysMenu> findByRoles(Set<Long> roleIds) {
        return roleMenuService.findMenusByRoleIds(null, roleIds, null);
    }

    /**
     * 查询所有菜单
     *
     * @return 所有菜单
     */
    public List<SysMenu> findAll() {
        return roleMenuService.findAll(null);
    }

    /**
     * 角色分配菜单
     *
     * @param roleId  角色id
     * @param menuIds 菜单id
     */
    @Transactional(rollbackFor = Throwable.class)
    public void setMenuToRole(Long roleId, Set<Long> menuIds) {
        roleMenuService.delete(roleId, null);
        if (CollectionUtils.isNotEmpty(menuIds)) {
            List<RoleMenuEntity> roleMenu = new ArrayList<>(menuIds.size());
            menuIds.forEach(e -> {
                RoleMenuEntity entity = new RoleMenuEntity();
                entity.setMenuId(e);
                entity.setRoleId(roleId);
                roleMenu.add(entity);
            });
            roleMenuService.saveAll(roleMenu);
        }
    }

    /**
     * 查询所有菜单列表
     *
     * @return 响应
     */
    public List<MenuInfoResponse> pageList() {
        List<SysMenu> sysMenus = findAll();
        return MenuMapStruct.INSTANCE.sysMenuToInfoResponseList(sysMenus);
    }

    /**
     * 查询顶级菜单
     *
     * @return 响应
     */
    public List<MenuInfoResponse> findOnes() {
        List<SysMenu> menusOnes = menuService.findMenusByType(null, 1);
        return MenuMapStruct.INSTANCE.sysMenuToInfoResponseList(menusOnes);
    }

    /**
     * 查询用户id菜单树
     *
     * @param userId 用户id
     * @param type   类型
     * @return 菜单树
     */
    public List<MenuTreeResponse> findTreeByUserId(Long userId, Integer type) {
        List<MenuTreeResponse> treeResponses = findByUserId(userId, type);
        return treeBuilder(treeResponses);
    }

    /**
     * 查询当前用户菜单
     *
     * @param userId 用户id
     * @param type   类型
     * @return 菜单树
     */
    public List<MenuTreeResponse> findByUserId(Long userId, Integer type) {
        List<SysRole> userRole = roleUserService.findRolesByUserId(null, userId);
        Optional<SysRole> adminRole = Optional.ofNullable(userRole).orElse(Collections.emptyList()).stream()
                .filter(e -> CommonConstant.ADMIN_ROLE_NAME.equals(e.getCode())).findAny();
        List<SysMenu> sysMenus = menuService.findByUserId(null, userId, type, adminRole.isPresent());
        return MenuMapStruct.INSTANCE.sysMenuVoToTreeResponses(sysMenus);
    }

    /**
     * 创建菜单
     *
     * @param request 请求
     * @return 响应
     */
    public Boolean create(MenuCreateRequest request) {
        this.checkMenuExist(request);
        MenuEntity menuEntity = MenuMapStruct.INSTANCE.createToMenu(request);
        if (Objects.isNull(menuEntity.getParentId())) {
            menuEntity.setParentId(-1L);
        }
        menuEntity.setCreatedAt(new Date());
        menuEntity.setUpdatedAt(new Date());
        return menuService.save(menuEntity);
    }

    /**
     * 更新菜单
     *
     * @param id      菜单id
     * @param request 请求
     * @return
     */
    public Boolean update(Long id, MenuUpdateRequest request) {
        MenuEntity sysMenuEntity = menuService.getById(id);
        if (sysMenuEntity == null) {
            throw new APIRuntimeException(ResultCodeEnum.MENU_NOT_EXIST);
        }
        Integer type = request.getType();
        if (MenuTypeEnums.MENU.getCode().equals(type) || MenuTypeEnums.DIRECTORY.getCode().equals(type)) {
            if (!sysMenuEntity.getCode().equals(request.getCode())) {
                this.checkMenuExist(request);
            }
        }
        if (MenuTypeEnums.BUTTON.getCode().equals(type)) {
            if (!sysMenuEntity.getUrl().equals(request.getUrl())
                    && !sysMenuEntity.getUrlMethod().equals(request.getUrlMethod())) {
                this.checkMenuExist(request);
            }
        }
        MenuEntity menuEntity = MenuMapStruct.INSTANCE.updateToMenu(request);
        menuEntity.setId(id);
        menuEntity.setCreatedBy(sysMenuEntity.getCreatedBy());
        menuEntity.setCreatedAt(sysMenuEntity.getCreatedAt());
        menuEntity.setUpdatedAt(new Date());
        menuEntity.setUpdatedBy(MenuEntity.DEFAULT_USERNAME);
        return menuService.save(menuEntity);
    }

    /**
     * 校验菜单是否存在
     *
     * @param request 请求
     * @param <T>     vo
     */
    public <T extends BaseMenuVO> void checkMenuExist(T request) {
        Integer type = request.getType();
        if (MenuTypeEnums.MENU.getCode().equals(type) || MenuTypeEnums.DIRECTORY.getCode().equals(type)) {
            // 校验名称是否重复
            SysMenu sysMenu = menuService.findByCode(request.getApp(), request.getCode());
            if (Objects.nonNull(sysMenu)) {
                throw new APIRuntimeException(ResultCodeEnum.MENU_NAME_EXIST);
            }
        }
        if (MenuTypeEnums.BUTTON.getCode().equals(type)) {
            // 校验url是否重复
            SysMenu sysMenu = menuService.findByUrlAndMethod(request.getApp(), request.getUrl(), request.getUrlMethod());
            if (Objects.nonNull(sysMenu)) {
                throw new APIRuntimeException(ResultCodeEnum.MENU_URL_EXIST);
            }
        }
    }

    /**
     * 根据菜单url和方法查询有权限的用户ID
     *
     * @param url       url
     * @param urlMethod url方法
     * @return 响应
     */
    public List<SysUserId> findUserByMenuUrl(String url, String urlMethod) {
        return menuService.findUserByMenuUrl(url, urlMethod);
    }

    public Set<Resource> findAllByType(Integer code) {
        return menuService.findAllByType(code);
    }

    public Set<Resource> queryInRoleIdAndType(Set<Long> roleIds, Integer menuType) {
        return menuService.queryInRoleIdAndType(roleIds, menuType);
    }
}
